﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

/**
 * (*1) 
 * ShaderVariableがglResourcesを使わずOpenGLのリソース(location)を直接保持しているのは
 * Object3Dを継承して無くglResourcesに代入できないから。
 * 美しくないが現状ではやむを得ない。
 * */


namespace M3G {
    public enum ShaderVariableType {
        Float = 320,
        Bool = 321,
        Int = 322,
        Vec2 = 323,
        Vec3 = 324,
        Vec4 = 325,
        BVec2 = 326,
        BVec3 = 327,
        BVec4 = 328,
        IVec2 = 329,
        IVec3 = 330,
        IVec4 = 331,
        Mat2 = 332,
        Mat3 = 333,
        Mat4 = 334,
        Mat3x4 = 335,
        Sampler2D = 336,
        SamplerCube = 337
    }

    public static class ShaderVariableTypeExtensions {

        /**
         * メモ:
         * vec2,3,4も実は1じゃないか? 実装間違えたかも
         * */
        public static int ToElementCount (this ShaderVariableType type) {
            switch (type) {
                case ShaderVariableType.Float: return 1;
                case ShaderVariableType.Bool: return 1;
                case ShaderVariableType.Int: return 1;
                case ShaderVariableType.Vec2: return 2;
                case ShaderVariableType.Vec3: return 3;
                case ShaderVariableType.Vec4: return 4;
                case ShaderVariableType.BVec2: return 2;
                case ShaderVariableType.BVec3: return 3;
                case ShaderVariableType.BVec4: return 4;
                case ShaderVariableType.IVec2: return 2;
                case ShaderVariableType.IVec3: return 3;
                case ShaderVariableType.IVec4: return 4;
                case ShaderVariableType.Mat2: return 1;     // Transform
                case ShaderVariableType.Mat3: return 1;     // Transform
                case ShaderVariableType.Mat4: return 1;     // Transform
                case ShaderVariableType.Mat3x4: return 1;     // Transform
                case ShaderVariableType.Sampler2D: return 1;     // Texture
                case ShaderVariableType.SamplerCube: return 1;   // Texture
                default: return 0;
            }
        }
    }

    public class ShaderVariable {
        #region Constant
        #endregion

        #region Field
        ShaderVariableType type;
        string             name;
        int                arraySize;
        object[]           values;
        int                location;
        #endregion


        #region Method
        /**
         * スカラー値の時はarraySize=0
         * */
        internal ShaderVariable (string name, ShaderVariableType type, int arraySize, int location) {
            if (name == null) {
                throw new ArgumentNullException ("Name is null");
            }
            if (type < ShaderVariableType.Float || type > ShaderVariableType.SamplerCube) {
                throw new ArgumentException ("Type must be Float,Bool,Int,Vec2,Vec3,Vec4,BVec2,BVec3,BVec4,Ivec2,IVec3,IVec3,IVec4,Mat2,Mat3,Mat4,Mat3x4,Sampler2D,SamplerCube, type=" + type);
            }
            if (arraySize < 0) {
                throw new ArgumentException ("Length is must be within [0,inf], len=" + arraySize);
            }

            this.type = type;
            this.arraySize = arraySize;
            this.name = name;
            this.location = location;
            this.values = new object[Math.Max(arraySize, 1) * type.ToElementCount()];
        }

        public ShaderVariableType Type {
            get { return type; }
        }

        public int ArraySize {
            get { return arraySize; }
        }

        public int Length {
            get { return values.Length; }
        }

        public string Name {
            get { return name; }
        }

        internal int Location {
            get { return location; }
        }

        public override string ToString () {
            return "name=" + name + ", type=" + type + ", length=" + ArraySize + ", location=" + location;
        }

        public object[] Values {
            get { return (object[])values.Clone(); }
        }

        public void SetValue (int i, object value) {
             this.values[i] = value; 
        }

        #endregion


    }
}
